package com.tender.shiro;

import com.tender.dto.ShiroUserDTO;
import com.tender.shiro.matcher.RetryLimitCredentialsMatcher;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import com.tender.ShiroUserBridgeBizService;
import com.tender.constants.Constants;
import com.tender.domain.ShiroUser;

import cn.hutool.json.JSONUtil;

import javax.annotation.PostConstruct;

@Slf4j
public class ShiroCustomRealm extends AuthorizingRealm {

    @Autowired
    private ShiroUserBridgeBizService shiroUserBridgeBizService;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 构造器，注入密码比较器
     */
    @PostConstruct
    // public ShiroCustomRealm() {
    public void initCredentialsMatcher() {
//        // 指定密码算法
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(Constants.HASH_ALGORITHM);
//        // 指定迭代次数
//        hashedCredentialsMatcher.setHashIterations(Constants.HASH_ITERATIONS);
//        // 生效密码比较器
//        setCredentialsMatcher(hashedCredentialsMatcher);

        // 指定密码算法
        RetryLimitCredentialsMatcher retryLimitCredentialsMatcher =
                new RetryLimitCredentialsMatcher(Constants.HASH_ALGORITHM, redissonClient);
        // 指定迭代次数
        retryLimitCredentialsMatcher.setHashIterations(Constants.HASH_ITERATIONS);
        // 生效密码比较器
        setCredentialsMatcher(retryLimitCredentialsMatcher);
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Object principal = SecurityUtils.getSubject().getPrincipal();
        log.info("执行授权逻辑");
        // 最终发现，是两个classLoader 不一样，所以不能强转
        System.out.println("**************** principal.getClass().getClassLoader() = " + principal.getClass().getClassLoader());
        System.out.println("**************** ShiroUserDTO.class.getClassLoader() = " + ShiroUserDTO.class.getClassLoader());
        // ShiroUser shiroUser = JSONUtil.toBean(JSONUtil.toJsonStr(principal), ShiroUser.class);

        ShiroUserDTO shiroUserDTO = JSONUtil.toBean(JSONUtil.toJsonStr(principal), ShiroUserDTO.class);
        log.info("doGetAuthorizationInfo ---------->  loginName = " + shiroUserDTO.getLoginName());

        return shiroUserBridgeBizService.getAuthorizationInfo(shiroUserDTO);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("执行认证逻辑");

        // 1.判断用户名
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        ShiroUser shiroUser = shiroUserBridgeBizService.findUserInfoByLoginName(token.getUsername());
        if (null == shiroUser) {
            // 用户不存在，shiro底层会抛出 UnKnowAccountException
            return null;
        }

        // 2.判断密码
        /**
         *  对比密码
         * 参数1：主体对象，按需要传，登陆成功后该参数可通过 SecurityUtils.getSubject().getPrincipal() 获取。
         * 参数2：从对象中取密码，users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对
         * 参数3：盐，可以为空
         * 参数4：当前 realm 的名字
         */
        return new SimpleAuthenticationInfo(shiroUser, shiroUser.getPassWord(), ByteSource.Util.bytes(shiroUser.getSalt()), getName());
    }

    @Override
    protected void doClearCache(PrincipalCollection principalCollection) {
        // 这么操作是因为 两个类对应的 ClassLoader 不一样，不能强转
        // ShiroUser shiroUser = JSONUtil.toBean(JSONUtil.toJsonStr(SecurityUtils.getSubject().getPrincipal()), ShiroUser.class);

        ShiroUserDTO shiroUserDTO = JSONUtil.toBean(JSONUtil.toJsonStr(SecurityUtils.getSubject().getPrincipal()), ShiroUserDTO.class);
        shiroUserBridgeBizService.clearCache(shiroUserDTO);

        super.doClearCache(principalCollection);
    }

}
